home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
pascal
/
turbig.zip
/
TURBIG.PAS
Wrap
Pascal/Delphi Source File
|
1985-09-28
|
24KB
|
639 lines
{
BigTurbo - Turbo Pascal large Code model support
**** FOR IBM PC And MSDOS machines only ****
**** Requires Turbo Pascal version 3.X ****
This file concatenates 8 files that should be separated
after download. The 8 files are as follows:
BIGTUR.DOC - describes the BigTurbo system
MAIN1.INC - include files for extra segment control
MAIN2.INC
FAR1.INC
FAR2.INC
MAINEXAM.PAS - main code segment example
FAREXAM.PAS - far code segment example
EXAM.GLO - shared global data declarations for example
}
{****************************************************************************}
{FOLLOWING IS BIGTUR.DOC}
********************************************************************
* BigTurbo - copyright (c) 1985 Kim Kokkonen, TurboPower Software. *
********************************************************************
* written 9/85. (408)-378-3672. Compuserve 72457,2131. *
* This version is a prototype for a commercial product. *
* It is hereby released to the public domain for personal, *
* non-commercial use only. *
* We will appreciate any feedback. *
********************************************************************
The BigTurbo system provides control to set up an extra 64K code
segment for Turbo Pascal programs. It provides an alternative to
overlays and chaining with some advantages over either technique.
The extra 64K code segment is written and compiled as a separate program
from the main code segment. The main block of the separate segment is
never used, unless you want to execute the program independently for
debug purposes. BigTurbo allows you to call the global procedures in the
extra code segment directly from the main program, or from procedures
in the main program. It also allows procedures in the extra code segment
to call each other and to call global procedures in the main code segment.
The two code segments share a common data segment, heap and stack, and
communication between the two segments is via global variables
(parameters are not allowed at present).
BigTurbo requires files MAIN1.INC and MAIN2.INC to be included and
compiled with the main program, and the files FAR1.INC and FAR2.INC to
be included and compiled with the code comprising the extra code segment.
As described below, the programmer must make minor modifications to
the include files MAIN2.INC and FAR2.INC to make them work in his/her
program.
The main program must have heap space reserved for the extra code
segment when it is compiled to a .COM file (the extra code segment
is loaded onto the heap of the main program). The extra code must be
compiled to a .COM file prior to executing the main program. The
code space, data space and heap/stack space options for the extra
code segment are never accessed and are therefore arbitrary.
Hereafter, we will call the extra code segment the "Far" code segment.
Communication between the Main code segment and the Far code segment
occurs only via global variables, similar to how Chain files are used.
As a result the Far program should contain a set of global data definitions
identical to those of the Main code segment. This is best managed by
putting the global definitions in an include file, and including that
file in both the Main and Far programs.
Improvements of BigTurbo over using Chain files or Overlays are as follows:
1) The Far code segment is loaded into memory only once, and remains
there until the program completes. You get a full extra 64K segment
(less runtime library overhead, about 10K) to use.
2) Any global procedure in the Far code segment may be called directly
either from the main code segment or from within the Far code
segment.
3) Any global procedure in the Main code segment may be called directly
either from the Far code segment or from within the main code
segment (as usual).
4) Upon completion of a call to the Far code segment, control returns
to the calling procedure. Calls in the other direction work the
same way.
5) The Main and Far code share the same heap and data segment, which
provides adequate means of communication between the two.
6) The restrictions on what can call what are fewer than for overlays.
7) You can do the equivalent of overlays by DISPOSing of the code
on the heap and loading another file's worth of code there. Only
one extra code segment is allowed at a time, although the techniques
here can clearly be extended to more segments with some logistic
headaches.
8) These techniques are not specific to any particular version of
Turbo, although we have assumed version 3.X (of any flavor).
Limitations of BigTurbo are as follows:
1) For now, calls across the code segment boundary may not use
parameters, and must be made only to procedures (not functions).
2) The programmer must do a small amount of manual management to keep
the required addressing tables set up (see SetupJumpTable below).
3) An extra copy of the Turbo runtime library is loaded into
memory for the Far code segment. This costs about 10K bytes of
RAM.
4) The Far code may not use global variables that are not used in the
Main code segment, and vice versa. The order of the global variable
definitions must be identical in each to guarantee proper addressing,
just as with chain files.
5) The Far code should not use EXTERNAL procedures or INLINE code that
enters and exits the global procedures in any way but the Turbo Pascal
standard.
6) Stack checking is not currently implemented for Far calls. Such
checking once implemented will be compiler version specific.
7) Far calls are somewhat slower than near calls (see the code in
FarCallHandler and MainCallHandler to judge the overhead).
8) As with Turbo overlays, runtime errors will provide misleading
addresses when they occur in the Far segment.
9) This hasn't been tested with overlays used being simultaneously, and
we have a feeling it wouldn't work well.
Other Comments:
1) The procedures in the Far code segment may contain subprocedures,
local variables, and typed constants just as usual. Calls to the
subprocedures may use parameters as usual. A Far procedure may
call itself recursively if desired.
2) The main executable block of the Far program may be empty. It will
never be accessed, unless you are using it to debug the Far procedures
independently.
3) The Far code is loaded into a location on the heap of the Main program.
You need to reserve this amount of memory when you compile the
Main program.
4) Your program should use the {$V-} directive to avoid problems in
string length type checking with these routines.
5) MAIN1.INC and MAIN2.INC add about 1800 bytes of code to the main
segment. FAR1.INC and FAR2.INC add about 700 bytes of code to
the far segment.
*******************************************************
* See the example files *
* MAINEXAM.PAS *
* FAREXAM.PAS *
* EXAM.GLO *
* for proper usage of the following include files *
*******************************************************
{END OF BIGTUR.DOC}
{****************************************************************************}
{FOLLOWING IS MAIN1.INC}
CONST
{set these values based on the size of the MAIN program}
MaxNumProcs = 10; {maximum number of procedures available for Far calls}
MaxProcNameLength = 20; {maximum name length of such procedures}
TYPE
ProcNameArray = ARRAY[1..MaxNumProcs] OF STRING[MaxProcNameLength];
ProcOffsetArray = ARRAY[1..MaxNumProcs] OF Integer;
BigTurboString = STRING[64];
JumpRecord = RECORD
offset, segment : Integer;
END;
CONST
{these need to be adjusted based on the value in MaxNumProcs}
{no literal values need be filled in here}
pnames : ProcNameArray = ('', '', '', '', '', '', '', '', '', '');
poffsets : ProcOffsetArray = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
VAR
FarCodePtr : ^integer; {will hold pointer to where far code is stored}
MainHand : JumpRecord; {holds pointer to MainCallHandler in this code segment}
FarJumpSet : JumpRecord; {holds pointer to SetupJumpTable in Far code segment}
FarHand : JumpRecord; {holds pointer to FarCallHandler in Far code segment}
PROCEDURE MakeLongCall(FarProcName : BigTurboString);
{-pass control to the call handler in the Far code}
BEGIN
INLINE(
$8C/$D0/ {MOV AX,SS}
$8E/$C0/ {MOV ES,AX}
$8B/$F5/ {MOV SI,BP}
$81/$C6/$04/$00/ {ADD SI,0004}
$FF/$1E/FarHand {CALL FAR FarHand}
);
END; {MakeLongCall}
{END OF MAIN1.INC}
{****************************************************************************}
{FOLLOWING IS MAIN2.INC}
PROCEDURE SetupJumpTable;
{-initialize the names and offsets of the near procedures}
{
***************************************************
* The programmer must maintain the lists below to *
* include all global procedure names that will be *
* called from the Far code segment. *
***************************************************
}
BEGIN
{
*** EXAMPLES and COMMENTS ********************************************
* Order is unimportant, except that procedures which are called most *
* often from far segment should be first in list. *
* Case of the string is important. Must be same as string passed to *
* MakeLongCall (avoids overhead time of uppercasing every call). *
* Fill in your own procedure names below. *
**********************************************************************
}
pnames[1] := 'mainproc1';
poffsets[1] := Ofs(mainproc1);
pnames[2] := 'mainproc2';
poffsets[2] := Ofs(mainproc2);
END; {SetupJumpTable}
PROCEDURE MainCallHandler;
{-pick up control from a far call and transfer to near procedure}
VAR
i : Integer;
procofs : Integer;
procname : BigTurboString;
BEGIN
{get procname from the es:si pointer passed in}
INLINE(
$31/$C9/ {XOR CX,CX}
$26/ {ES: }
$8A/$0C/ {MOV CL,[SI]}
$FE/$C1/ {INC CL}
$BF/procname/ {MOV DI,ofs(procname)}
$FC/ {CLD }
{10B:}
$26/ {ES: }
$AC/ {LODSB }
$88/$03/ {MOV [BP+DI],AL}
$47/ {INC DI}
$E2/$F9 {LOOP 010B}
);
{match against the stored procnames}
i := 0;
REPEAT
i := i+1;
UNTIL (i > MaxNumProcs) OR (pnames[i] = procname);
{error check}
IF i > MaxNumProcs THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far procedure ', procname, ' not found....');
Halt;
END;
{check for stack space, later}
{assure stack aligned for parameter passing, later}
{call the procedure}
procofs := poffsets[i];
INLINE(
$C4/$46/< procofs/ {LES AX,procofs[BP]}
$FF/$D0 {CALL AX}
);
{restore stack frame and FAR return}
INLINE(
$8B/$E5/ {mov sp,bp}
$5D/ {pop bp}
$CB {ret far}
);
END; {MainCallHandler}
PROCEDURE LoadFarCode(FarComFileName : BigTurboString);
{-load the FarComFile and set up the required addresses}
CONST
id1string : BigTurboString = 'FARCALLHANDLER FOLLOWS';
id2string : BigTurboString = 'SETJUMPTABLE FOLLOWS';
VAR
f : FILE;
len : Byte ABSOLUTE FarComFileName;
i : Integer;
size : Integer;
FarCodeSeg : Integer;
FarCodeOfs : Integer;
ext : STRING[4];
testlen : Byte;
teststring : BigTurboString;
BEGIN
{assure it is a .COM file}
ext := Copy(FarComFileName, len-3, 4);
FOR i := 1 TO 4 DO ext[i] := UpCase(ext[i]);
IF ext <> '.COM' THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far Code File must be a .COM file');
Halt;
END;
{make sure it exists and open it}
Assign(f, FarComFileName);
{note we are using a block size of 1}
{$I-} Reset(f, 1); {$I+}
IF IOResult <> 0 THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far Code File not found....');
Halt;
END;
{make sure there is something in it}
size := FileSize(f);
IF size = 0 THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far Code File is empty....');
Halt;
END;
{make sure there is space for it}
IF (size SHR 4) > (MaxAvail-32) THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far Code too large to load on heap....');
Close(f);
Halt;
END;
{allocate memory on heap}
GetMem(FarCodePtr, size+512);
{IMPORTANT: normalize seg and ofs so that we can make farcodeofs=$100}
FarCodeSeg := Seg(FarCodePtr);
FarCodeOfs := Ofs(FarCodePtr);
WHILE FarCodeOfs > $100 DO BEGIN
FarCodeOfs := FarCodeOfs-16;
FarCodeSeg := FarCodeSeg+1;
END;
FarCodePtr := Ptr(FarCodeSeg, $100);
FarCodeOfs := $100;
{load code onto heap}
BlockRead(f, farcodeptr^, size);
Close(f);
{store the pointers}
FarJumpSet.segment := FarCodeSeg;
FarHand.segment := FarCodeSeg;
{store the local addresses}
MainHand.offset := Ofs(MainCallHandler);
MainHand.segment := CSeg;
{search the far code for the idstrings identifying key offsets}
{start at the top and work backwards, should be faster}
i := size;
testlen := Length(id1string)+1;
REPEAT
i := i-1;
Move(Mem[FarCodeSeg:(FarCodeOfs+i)], teststring, testlen);
UNTIL (i < 1) OR (teststring = id1string);
IF i < 1 THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'ID string ', id1string, ' not found in Far code....');
Halt;
END;
{store the adjusted offset for far calls}
FarHand.offset := FarCodeOfs+i-7;
testlen := Length(id2string)+1;
REPEAT
i := i-1;
Move(Mem[FarCodeSeg:(FarCodeOfs+i)], teststring, testlen);
UNTIL (i < 1) OR (teststring = id2string);
IF i < 1 THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'ID string ', id2string, ' not found in Far code....');
Halt;
END;
{store the adjusted offset for far calls}
FarJumpSet.offset := FarCodeOfs+i-7;
{set up the local jump table}
SetupJumpTable;
{set up the far jump table}
INLINE(
$FF/$1E/FarJumpSet {CALL FAR FarJumpSet}
);
{we are done}
END; {LoadFarCode}
{END OF MAIN2.INC}
{****************************************************************************}
{FOLLOWING IS FAR1.INC}
CONST
{set these values based on the size of the FAR program}
{see comments in MAIN1.INC}
MaxNumProcs = 10;
MaxProcNameLength = 20;
TYPE
ProcNameArray = ARRAY[1..MaxNumProcs] OF STRING[MaxProcNameLength];
ProcOffsetArray = ARRAY[1..MaxNumProcs] OF Integer;
BigTurboString = STRING[64];
JumpRecord = RECORD
offset, segment : Integer;
END;
CONST
{these need to be adjusted based on the value in MaxNumProcs}
{no literal values need be filled in here}
pnames : ProcNameArray = ('', '', '', '', '', '', '', '', '', '');
poffsets : ProcOffsetArray = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
VAR
FarCodePtr : ^integer; {will hold pointer to where far code is stored}
MainHand : JumpRecord; {holds pointer to MainCallHandler in the other code segment}
FarJumpSet : JumpRecord; {holds pointer to SetupJumpTable in this code segment}
FarHand : JumpRecord; {holds pointer to FarCallHandler in this code segment}
PROCEDURE MakeLongCall(FarProcName : BigTurboString);
{-pass control to the call handler in the Far code}
BEGIN
INLINE(
$8C/$D0/ {MOV AX,SS}
$8E/$C0/ {MOV ES,AX}
$8B/$F5/ {MOV SI,BP}
$81/$C6/$04/$00/ {ADD SI,0004}
$FF/$1E/MainHand {CALL FAR MainHand}
);
END; {MakeLongCall}
{END OF FAR1.INC}
{****************************************************************************}
{FOLLOWING IS FAR2.INC}
PROCEDURE SetupJumpTable;
{-initialize the names and offsets of the near procedures}
{
***************************************************
* The programmer must maintain the lists below to *
* include all global procedure names that may be *
* called from the MAIN code segment. *
* This procedure should never be called directly *
* within its own program. *
***************************************************
}
CONST
{following string must be here to allow address to be found from Main Code}
idstring : BigTurboString = 'SETJUMPTABLE FOLLOWS';
BEGIN
{
*** EXAMPLES and COMMENTS ********************************************
* Order is unimportant, except that procedures which are called most *
* often from far segment should be first in list. *
* Case of the string is important. Must be same as string passed to *
* MakeLongCall (avoids overhead time of uppercasing every call). *
* Fill in your own procedure names below. *
**********************************************************************
}
pnames[1] := 'farproc1';
poffsets[1] := Ofs(farproc1);
pnames[2] := 'farproc2';
poffsets[2] := Ofs(farproc2);
{restore stack and do a FAR return to the main segment}
INLINE(
$8B/$E5/ {mov sp,bp}
$5D/ {pop bp}
$CB {ret far}
);
END; {SetupJumpTable}
PROCEDURE FarCallHandler;
{-pick up control from a far call and transfer to near procedure}
CONST
{following string must be here to allow address to be found from Main Code}
idstring : BigTurboString = 'FARCALLHANDLER FOLLOWS';
VAR
i : Integer;
procofs : Integer;
procname : BigTurboString;
BEGIN
{get procname from the es:si pointer passed in}
INLINE(
$31/$C9/ {XOR CX,CX}
$26/ {ES: }
$8A/$0C/ {MOV CL,[SI]}
$FE/$C1/ {INC CL}
$BF/procname/ {MOV DI,ofs(procname)}
$FC/ {CLD }
$26/ {ES: }
$AC/ {LODSB }
$88/$03/ {MOV [BP+DI],AL}
$47/ {INC DI}
$E2/$F9 {LOOP 010B}
);
{match against the stored procnames}
i := 0;
REPEAT
i := i+1;
UNTIL (i > MaxNumProcs) OR (pnames[i] = procname);
{error check}
IF i > MaxNumProcs THEN BEGIN
WriteLn(Con);
WriteLn(Con, 'Far procedure ', procname, ' not found....');
Halt;
END;
{check for stack space, later}
{call the procedure}
procofs := poffsets[i];
INLINE(
$C4/$46/< procofs/ {LES AX,procofs[BP]}
$FF/$D0 {CALL AX}
);
{restore stack and FAR return}
INLINE(
$8B/$E5/ {mov sp,bp}
$5D/ {pop bp}
$CB {ret far}
);
END; {FarCallHandler}
{END OF FAR2.INC}
{****************************************************************************}
{FOLLOWING IS MAINEXAM.PAS}
{$V-}
PROGRAM mainexam;
{-demonstrate BigTurbo techniques}
{first BigTurbo include file - before anything else}
{$I main1.inc}
{all your global variables and declarations are in following file}
{$I exam.glo}
{your own procedures follow}
PROCEDURE mainproc1;
{-demonstrate getting to and from the extra segment}
BEGIN
MakeLongCall('farproc2');
END; {mainproc1}
PROCEDURE mainproc2;
{-demonstrate getting to and from the extra segment}
BEGIN
WriteLn('got to mainproc2 from far segment');
END; {mainproc1}
{second BigTurbo include file - after all other procedures}
{$I main2.inc}
BEGIN
{load far code}
LoadFarCode('farexam.com');
{your code follows}
WriteLn('in main program');
WriteLn('calling local procedure mainproc1');
mainproc1;
WriteLn('back from mainproc1');
END.
{END OF MAINEXAM.PAS}
{****************************************************************************}
{FOLLOWING IS FAREXAM.PAS}
{$V-}
PROGRAM farexam;
{-demonstrate BigTurbo techniques}
{this file holds code for the extra segment}
{first BigTurbo include file - before everything else}
{$I far1.inc}
{all your global variables and declarations are in following file}
{$I exam.glo}
{your procedures follow}
PROCEDURE farproc1;
{-demonstrate a local call in the extra segment}
BEGIN
WriteLn('got to farproc1');
WriteLn('calling mainproc2 in main code segment');
MakeLongCall('mainproc2');
WriteLn('back from mainproc2');
END; {farproc1}
PROCEDURE farproc2;
{-just demonstrate getting here from main segment}
BEGIN
WriteLn('got to farproc2 from main code');
WriteLn('calling local procedure farproc1 in far code');
farproc1;
WriteLn('returned from farproc1');
END; {farproc2}
{second BigTurbo include file - after all your procedures}
{$I far2.inc}
BEGIN
{this block should normally be empty}
{you can use it to directly call procedures in this program during debugging}
END.
{END OF FAREXAM.PAS}
{****************************************************************************}
{FOLLOWING IS EXAM.GLO}
{global definitions and declarations go here}
{example doesn't need any, so these are bogus}
TYPE
xxx = Integer;
VAR
i : xxx;
{END OF EXAM.GLO}
{****************************************************************************}
ger;
VAR
i : xxx;
{END OF EXAM.GLO}
{***************************